Skip to content

Code Runner Preliminary Containerization - #77

Closed
mataiodoxion wants to merge 12 commits into
Open-Coding-Society:mainfrom
CSA-Admin-OCS:code-runner-sec
Closed

Code Runner Preliminary Containerization#77
mataiodoxion wants to merge 12 commits into
Open-Coding-Society:mainfrom
CSA-Admin-OCS:code-runner-sec

Conversation

@mataiodoxion

@mataiodoxion mataiodoxion commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Runs the python code execution endpoint in a separate container if .env key IS_PRODUCTION=true, otherwise just runs the exec locally.

This fixes the main issue with reading sensitive contents of the main flask-web container like sending:

{
  "code": "import os; os.system('cat /app/.env')"
}

which is now just met with (because it's a different container)

{
  "output": "cat: /app/.env: No such file or directory\n"
}

It might be beneficial to work on some more container hardening (especially for the kernel to prevent escapes), and setting limits for resource exhaustion, but this fixes the most glaring issues for now.

RudraBJoshi and others added 5 commits August 19, 2026 20:20
POST /api/internal/sync-password: updates a user's password by uid,
called server-to-server by the Spring backend after it completes an
OAuth + student ID verified password reset, so the same account's Flask
password doesn't drift out of sync with Spring's.

Gated by a shared secret (INTERNAL_SYNC_KEY, compared with
hmac.compare_digest for timing-safety) instead of user auth, since this
is never called from a browser -- there's no existing service-to-service
auth mechanism in this app to reuse, and reusing the admin-only
PUT /api/user route would have meant giving Spring real Flask admin
credentials. This endpoint can only ever change one user's password, and
is a closed no-op if INTERNAL_SYNC_KEY is unset.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
GET /api/user (any logged-in user, not just admins) and the other UserAPI
create/update/delete responses were including the PBKDF2 hash from
User.read() in the JSON body. Adds _without_password() and applies it at
every general-purpose response site; the admin-only backup/export endpoints
in data_export_import_api.py are left alone since they need the hash for
restore fidelity.
*** REQUIRED BEFORE THIS DEPLOYS: production runs MySQL (see __init__.py --
SQLALCHEMY_DATABASE_URI switches to MySQL whenever DB_ENDPOINT/DB_USERNAME/
DB_PASSWORD are set), a completely separate database this session had no
access to. Only the local dev SQLite DB has been migrated. Someone MUST run
this against production before/with this deploy, or every login there will
error on the missing column:

    ALTER TABLE users ADD COLUMN token_version INTEGER NOT NULL DEFAULT 0;

***

Previously nothing tied an issued JWT or Flask-Login session to a specific
password: the JWT carried no exp claim at all (never expired by JWT
semantics) and no password-derived data, and Flask-Login sessions just
carried a bare user id, re-validated against fresh DB data on every request
but with no check that the underlying credential hadn't changed. A stolen
JWT or session cookie kept working indefinitely, surviving a password reset
that was meant to lock an attacker out.

Adds User.token_version, bumped in set_password() (the single funnel every
password-change path already goes through) only on an actual hash change.
JWTs now carry token_version + exp and are checked against the account's
current value in auth_required. Sessions now carry it via a composite
get_id() ("id:token_version"), checked in load_user (main.py), so a stale
session is rejected before ever reaching a @login_required route instead of
running with outdated auth state.

Verified live: fresh JWT/session -> 200, password reset -> old JWT gets 401
with an explicit "password has changed" message, old session gets redirected
to login, fresh login after the reset works again.
@jm1021

jm1021 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What about this? We should not have runners broken while we fix security.

todo: don't hardcode

RUNNER_URL = "http://code_runner:8591/python"

@mataiodoxion

Copy link
Copy Markdown
Contributor Author

Not necessarily broken; I suppose it would be better to add .env configuration of the code runner URL

@jm1021

jm1021 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Good thought on .env.

@jm1021

jm1021 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

I will close this until then, as it also has conflicts.

@jm1021 jm1021 closed this Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants